import requests
from lxml import etree
import re #正则表达式
from collections import Counter #统计数量
import sqlite3
#导入线程池
from multiprocessing.dummy import Pool
#jieba分词
from jieba import lcut
import jieba
jieba.initialize()  # 手动初始化（可选）

def main():
    #新闻种类数组:
    #学校要闻、综合新闻、媒体聚焦、理论学习、文明创建、专题网站
    #newsType = ["xxyw","zhxw","mtjj","llxx","wmcj","ztwz"]
    newsType = ["xxyw"]
    #1 爬取网页（根据新闻类型,分别获取信息）
    #第一种是学校官方新闻
    #第二种是引用外网新闻，只需要获取新闻标题和url（判断url是否包含whpu）
    pool = Pool(5)
    for newType in newsType:# 里面有7个新闻种类链接
        print("开始爬取"+newType)
        allList = [] #某类新闻的所有链接
        allList = getUrlList(newType)
        print("获取该类新闻链接成功！")
        #print(allList)
        #引入多线程
        #datalist = getData(allList) #这里的newType是为了保存到datalist
        '''
        第一个参数就是需要引用的函数，第二个参数是一个可迭代对象，如nums_list = [1, 2, 9]
        它会把需要迭代的元素一个个的传入第一个参数我们的函数中。
        因为我们的map会自动将数据作为参数传进去 ;result = pool.map(numsCheng, nums_list)
        '''
        #报错：getData需要数组，map是迭代为单个str
        datalist = pool.map(getOneData, allList)  # 函数，所有列表
        print("某一类新闻爬取成功！")
        #print(datalist)
        print("=========================================")
        #3 保存数据到sqlite数据库; 数据库名：news.db 表名：newsInfo
        dbpath = "news.db" #s数据库名字
        saveData(datalist, dbpath) #保存

    # 使用完成
    pool.close()  # pool.close告诉池不要接受任何新工作。
    pool.join()  # pool.join告诉池等待所有作业完成然后退出，有效地清理池。
    print("所有数据保存成功！")

#格式化新闻类型链接, 获得baseurl
#类型包括：学校要闻-xxyw；综合新闻-zhxw；媒体聚焦：mtjj；理论学习：llxx；文明创建:wmcj;专题网站：ztwz
def urlFormat(newsType, pageNum):
    baseurl = ""
    baseurl = rf"http://news.whpu.edu.cn/xw/{newsType}.htm"
    pageSize, _ = getTotalNum(baseurl) #获取当前类型新闻的总页数
    #若为第一页则不能添加页码，其他的都要添加
    if pageNum != pageSize:
        baseurl = rf"http://news.whpu.edu.cn/xw/{newsType}/{pageNum}.htm"
    return baseurl


#获得某种类型新闻的所有url
def getUrlList(newsType):
    all_url_list = [] # 存放某种类型的新闻数据
    # 总页数
    baseurl = rf"http://news.whpu.edu.cn/xw/{newsType}.htm"
    #有些链接被过滤了，不能直接使用page_nums???
    page_nums, _ = getTotalNum(baseurl)
    #183是测试数据，应该为 0
    # range(start, stop, [step])，每次加上-1，直到0，但是不 包含0
    # for i in range(page_nums, 0, -1): #page_nums到 1
    #########################测试：控制新闻数量#######################################################
    for i in range(page_nums, page_nums-5, -1): #倒数5页条新闻
    #########################测试：控制新闻数量#######################################################
        # 当前页码对应的页面
        url = urlFormat(newsType, i)
        # 获取网页html的text文本
        page_text = askURL(url)
        tree = etree.HTML(page_text)
        # 获得新闻链接列表
        urls = tree.xpath("//*[@id=\"content\"]/div[4]/ul//a/@href")
        #遍历每一条新闻url
        for url_one in urls:
            '''
            此处是格式化一种类型的某一页里面的所有url链接
            如果是外网的链接，就丢弃
            '''
            #print(url_one) href="../info/1005/17129.htm"
            url_one = str(url_one)
            if '../info' in url_one: ######################这个过滤很多bug#########################
                #页面的url格式不全，需要自动补齐
                url_one = url_one.replace('../..', 'http://news.whpu.edu.cn')
                url_one = url_one.replace('..', 'http://news.whpu.edu.cn')
                all_url_list.append(url_one)

    #清除重复的url链接，数据量是不是有点大？
    # 运用新建字典的方式，去除重复的键
    dic = {}
    # 字典在创建新的字典时，有重复key则覆盖
    all_url_list= dic.fromkeys(all_url_list).keys() #结果为字典
    return list(all_url_list)

'''
baseurl = "http://news.whpu.edu.cn/info/1007/16975.htm"
owner=1310939652 #不知道是什么。。。。一个编号====================自己获取
view = getView(baseurl,1310939652) #使用方式
'''
#获取新闻的浏览数量，owner默认为  1310939652
def getView(baseurl, owner):
    newNum = (baseurl.split("/")[-1]).split(".")[0] #16975
    viewUrl = rf"http://news.whpu.edu.cn/system/resource/code/news/click/dynclicks.jsp?clickid={newNum}&owner={owner}&clicktype=wbnews"
    #print(viewUrl)
    header = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36"}
    web_data = requests.get(url=viewUrl, headers = header)
    view =int(web_data.text)
    return view

#获取关键词，使用jieba分词=======慢慢慢=======
def getKeywords(content):
    #text = ''.join(sentences) #将列表中的每个成员以字符''分隔开再拼接成一个字符串
    #print(text)
    words = lcut(content) #分词
    #过滤分词后长度小于1的词语或标点符号，！！！！！！！！
    words = filter(lambda word: len(word) > 1, words)
    freq = Counter(words)  #统计次数
    res = freq.most_common(5)  #类型为list,一共5条
    return res

#方法一：参数为一条url
def getOneData(oneUrl):
    data = [] # 保存一篇新闻的全部信息包括：id,类型，url,标题，日期，浏览量，关键字
    #1 获取新闻的唯一标识id: http://news.whpu.edu.cn/info/1002/17024.htm则id为 17024
    urlId = oneUrl.split('/')[-1].split('.')[0]
    data.append(urlId)

    #########解析页面才能获得的数据###########
    # (2) 逐一解析数据
    html = askURL(oneUrl)  # 保存获取的网页源码text
    tree = etree.HTML(html)

    #2 保存传过来的新闻类型(动态加载：../../xw/ztwz.htm)。可以优化，直接获得中文类型：专题网站，而不是ztwz
    #//*[@id="content"]/div[1]/ul/li[3]/a
    types = tree.xpath("//*[@id=\"content\"]//ul/li[3]/a/@href")[0]
    types = types.split("/")[-1].split(".")[0] #获得ztwz字符串
    data.append(types)

    #3 保存新闻的链接url
    data.append(oneUrl)

    #4 新闻标题 [为什么有的标题出现\u200b校领导带？？？？]
    title = tree.xpath("//*[@id=\"content\"]/div[2]/form/div/ul/li[2]/text()")[0]
    data.append(title)

    #5 新闻时间:   发布日期：2022-03-22
    time = tree.xpath("//*[@id=\"content\"]/div[2]/form/div/ul/li[4]/span[3]/text()")[0]
    time = time.split("：")[1]
    data.append(time)

    #6 新闻浏览量
    #这个是动态加载出来的，cccc？？？？？？？？？？？？？？？
    #view = tree.xpath("//*[@id=\"content\"]/div[2]/form/div/ul/li[4]/span[4]/span/text()")
    view = getView(oneUrl, 1310939652) #int型????????
    data.append(view)

    #7 新闻关键字
    # 新闻内容
    texts = tree.xpath("//div[@class=\"text\"]//text()") #list类型
    # 将列表中的每个成员以字符''分隔开再拼接成一个字符串,(省略并strip 同时去掉左右两边的空格)
    texts = ''.join(texts)
    #清除\r\n
    texts = texts.replace('\r', '').replace('\n', '')
    keywords = getKeywords(texts)
    data.append(keywords)

    print(data)
    return data #只返回一条数据

#方法二：爬取网页，获取数据，解析数据; 参数为字符串数组
def getData(all_url_list):
    datalist = []
    #print(type(all_url_list)) #是str类型，根本不是字符串数组
    for url in all_url_list:
        data = [] # 保存一篇新闻的全部信息包括：id,类型，url,标题，日期，浏览量，关键字
        #1 获取新闻的唯一标识id: http://news.whpu.edu.cn/info/1002/17024.htm则id为 17024
        urlId = url.split('/')[-1].split('.')[0]
        data.append(urlId)

        #########解析页面才能获得的数据###########
        # (2) 逐一解析数据
        html = askURL(url)  # 保存获取的网页源码text
        tree = etree.HTML(html)

        #2 保存传过来的新闻类型(动态加载：../../xw/ztwz.htm)。可以优化，直接获得中文类型：专题网站，而不是ztwz
        types = tree.xpath("//*[@id=\"content\"]/div[1]/ul/li[3]/a/@href")[0]
        types = types.split("/")[-1].split(".")[0] #获得ztwz字符串
        data.append(types)

        #3 保存新闻的链接url
        data.append(url)

        #4 新闻标题 [为什么有的标题出现\u200b校领导带？？？？]
        title = tree.xpath("//*[@id=\"content\"]/div[2]/form/div/ul/li[2]/text()")[0]
        data.append(title)

        #5 新闻时间:   发布日期：2022-03-22
        time = tree.xpath("//*[@id=\"content\"]/div[2]/form/div/ul/li[4]/span[3]/text()")[0]
        time = time.split("：")[1]
        data.append(time)

        #6 新闻浏览量
        #这个是动态加载出来的，cccc？？？？？？？？？？？？？？？
        #view = tree.xpath("//*[@id=\"content\"]/div[2]/form/div/ul/li[4]/span[4]/span/text()")
        view = getView(url, 1310939652) #int型????????
        data.append(view)

        #7 新闻关键字
        # 新闻内容
        texts = tree.xpath("//div[@id=\"vsb_content_2\"]//text()") #list类型
        # 将列表中的每个成员以字符''分隔开再拼接成一个字符串,(省略并strip 同时去掉左右两边的空格)
        texts = ''.join(texts)
        #清除\r\n
        texts = texts.replace('\r', '').replace('\n', '')
        keywords = getKeywords(texts)
        data.append(keywords)
        #将一条新闻的相关信息存入列表
        datalist.append(data)

        #else:  #url为外网引用链接，只获取标题和url，难以获得id。？？？？
           # continue; #先丢弃先丢弃先丢弃先丢弃先丢弃先丢弃先丢弃
    print("一类新闻爬取成功！")
    return datalist


#获取某种类型新闻的总条数和总页数
def getTotalNum(baseurl):
    newNums = 0
    pageNums = 0
    page_text = askURL(baseurl) #获取页面html内容
    # 页面dom树
    tree = etree.HTML(page_text)
    #获取相关信息：【共3614条  1/181】
    page_str = tree.xpath("//*[@id=\"fanye153388\"]/text()")[0]
    #print(page_str)
    newNums = re.findall(r'[1-9]+', page_str)[0] # + ：匹配1个或多个的表达式
    pageNums = page_str.split('/')[-1]
    pageNums = int(pageNums)
    newNums = int(newNums)
    return pageNums, newNums


#得到指定的一个url的网页内容。返回页面html的text格式
def askURL(url):
    #模拟浏览器头部信息，向服务器发送信息
    header = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36"}
    html = ""
    try:
        #print(url)
        html = requests.get(url=url, headers=header)
    except Exception as e:
        print(e)

    html.encoding = "utf-8"
    html = html.text
    return html



#3 保存数据（首先要初始化数据库）
def saveData(datalist,dbpath):
    #初始化数据库（暂时不需要）
    #init_db(dbpath)
    #1 连接数据库，获取游标
    conn = sqlite3.connect(dbpath)
    cur = conn.cursor()
    #2 获取sql语句
    sqlList = getSql(datalist)
    for sql in sqlList:
        cur.execute(sql)
    conn.commit()
    print("保存数据到数据库成功")
    conn.close()


#处理datalist列表里面所有的数据，返回sql语句的list集合
def getSql(datalist):
    sqlList = []
    for data in datalist:
        #index：列的下标  len(data)多少列
        for index in range(len(data)):
            #由于有的数据存入数据库需要的数据类型为str，使用需要转换
            if index==0: #id为str，需要转换为int
                data[index] = int(data[index])
            #[('工作', 28), ('学生', 25), ('坚持', 11), ('大学生', 9), ('调研', 6)]
            if index==6: #关键字为list类型，里面是什么类型
                keywords = ""
                for key,value in data[index]: #key为str类型，value为int类型
                    keyword = key+':'+str(value) #str类型
                    keywords = keyword+' '+keywords #空格相隔
                data[index] = keywords #list成功变成字符串

        sql = "insert or replace into newsInfo(id,type,url,title,day,views,keywords)values(%d,'%s','%s','%s','%s',%d,'%s')" % (data[0],data[1],data[2],data[3],data[4],data[5],data[6])
        sqlList.append(sql)

    return sqlList

# 创建数据库
def init_db(dbpath):
    sql = '''
        create table newsInfo(
            id int primary key not null,
            type varchar,
            url varchar,
            title varchar,
            day varchar,
            views int,
            keywords varchar);
    '''
    conn = sqlite3.connect(dbpath)
    cursor = conn.cursor()
    cursor.execute(sql)
    conn.commit()
    print("创建数据库成功")
    conn.close()


if __name__ == '__main__':

   #init_db("news.db") #只能执行一次


   main()
